﻿//Original source (c) Andrey Shchekin, http://www.codeproject.com/KB/aspnet/TypedRepeater.aspx

using System;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Web.UI;

namespace MvcImproved.Generic
{
    /// <summary>
    ///     The GenericControlTypeDelegator is a simple type delegator for controls
    ///     that can be used to override calls to properties to pass back a property
    ///     delegator that applies correctly typed dynamic data item container
    ///     declarations to the type.
    ///     
    ///     This in turn enables start-finish strong-typing of the ASP .NET data containers.
	///     
	///		None of this would be necessary if C# allowed for generics in custom attributes...
    /// </summary>
    public class GenericControlTypeDelegator : TypeDelegator
    {
        /// <summary>
        ///     Initialize the generic control type delegator with the
        ///     specific concrete type.
        /// </summary>
        /// <param name="typeToDelegateFor">Type to delegate for.</param>
        public GenericControlTypeDelegator(Type typeToDelegateFor)
            : base(typeToDelegateFor)
        {
        }

		public override bool IsGenericType
		{
			get
			{
				return this.typeImpl.IsGenericType;
			}
		}
		public override Type[] GetGenericArguments()
		{
			return this.typeImpl.GetGenericArguments();
		}
		public override bool ContainsGenericParameters
		{
			get
			{
				return this.typeImpl.ContainsGenericParameters;
			}
		}

        /// <summary>
        ///     Get the property implementation for the specified property.
        /// </summary>
        /// <param name="name">Name</param>
        /// <param name="bindingAttr">Binding Attributes</param>
        /// <param name="binder">Biding context</param>
        /// <param name="returnType">Return type</param>
        /// <param name="types">Types for parameters</param>
        /// <param name="modifiers">Modifiers for the parameters</param>
        /// <returns>Property information, if found.</returns>
        protected override PropertyInfo GetPropertyImpl(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers)
        {
            PropertyInfo baseProperty = base.GetPropertyImpl(name, bindingAttr, binder, returnType, types, modifiers);
            if (baseProperty != null)
                return new GenericControlPropertyDelegator(this.typeImpl, baseProperty);
            else
                return null;
        }

        /// <summary>
        ///     The GenericControlPropertyDelegator answers any call for a TemplateContainer attribute
        ///     with a directed generic response.
        /// </summary>
        internal class GenericControlPropertyDelegator : PropertyInfo 
        {
            private PropertyInfo _WrappedPropertyInstance;
            private Type _GenericControlType;

            /// <summary>
            ///     Initialize the generic control property delegator with 
            /// </summary>
            /// <param name="genericType">Generic type to wrap properties for</param>
            /// <param name="property">Real property being wrapped</param>
            public GenericControlPropertyDelegator(Type genericType, PropertyInfo property)
            {
                _GenericControlType = genericType;
                _WrappedPropertyInstance = property;
            }

            /// <summary>
            ///     Get the custom attributes of the specified type, searching the inheritance 
            ///     chain as appropriate.
            /// </summary>
            /// <param name="attributeType">Attribute type.</param>
            /// <param name="inherit">Search inheritance hierarchy?</param>
            /// <returns>Collection of matching attributes</returns>
            public override object[] GetCustomAttributes(Type attributeType, bool inherit)
            {
                // Intercept calls for template container attributes
                if (typeof(TemplateContainerAttribute) == attributeType)
                {
					GenericTemplateContainerAttribute parameters = _WrappedPropertyInstance.GetCustomAttributes(typeof(GenericTemplateContainerAttribute), true).OfType<GenericTemplateContainerAttribute>().FirstOrDefault();
                    if (parameters != null)
                    {
                        Type[] parentTypeParameters = _GenericControlType.GetGenericArguments();
                        Type[] mapped = parameters.GenericParameters.Select(x => parentTypeParameters[x]).ToArray();
                        Type containerType = parameters.ContainerType.MakeGenericType(mapped);
                        return new Attribute[] { new TemplateContainerAttribute(containerType) };
                    }
                }

                // Pass through to original implementation for other attribute types.
                return this._WrappedPropertyInstance.GetCustomAttributes(attributeType, true);
            }


            public override object GetValue(object obj, BindingFlags invokeAttr, Binder binder, object[] index, CultureInfo culture) 
            { 
                return this._WrappedPropertyInstance.GetValue(obj, invokeAttr, binder, index, culture); 
            }  
            
            public override void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, object[] index, CultureInfo culture) 
            { 
                this._WrappedPropertyInstance.SetValue(obj, value, invokeAttr, binder, index, culture); 
            }  
            
            public override ParameterInfo[] GetIndexParameters() 
            { 
                return this._WrappedPropertyInstance.GetIndexParameters(); 
            }  
            
            public override MethodInfo[] GetAccessors(bool nonPublic) 
            { 
                return this._WrappedPropertyInstance.GetAccessors(nonPublic); 
            }  
            
            public override MethodInfo GetGetMethod(bool nonPublic) 
            { 
                return this._WrappedPropertyInstance.GetGetMethod(nonPublic); 
            }  
            
            public override MethodInfo GetSetMethod(bool nonPublic) 
            { 
                return this._WrappedPropertyInstance.GetSetMethod(nonPublic); 
            }  
            
            public override bool IsDefined(Type attributeType, bool inherit) 
            { 
                return this._WrappedPropertyInstance.IsDefined(attributeType, true); 
            }  
            
            public override object[] GetCustomAttributes(bool inherit) 
            { 
                return this._WrappedPropertyInstance.GetCustomAttributes(inherit); 
            }  

            public override string Name
            {
                get
                {
                    return this._WrappedPropertyInstance.Name;
                }
            }

            public override Type PropertyType
            {
                get
                {
                    return this._WrappedPropertyInstance.PropertyType;
                }

            }

            public override bool CanRead
            {
                get
                {
                    return this._WrappedPropertyInstance.CanRead;
                }
            }

            public override bool CanWrite
            {
                get
                {
                    return this._WrappedPropertyInstance.CanWrite;
                }
            }

            public override Type DeclaringType
            {
                get
                {
                    return this._WrappedPropertyInstance.DeclaringType;
                }
            }

            public override Type ReflectedType
            {
                get
                {
                    return this._WrappedPropertyInstance.ReflectedType;
                }
            }

            public override PropertyAttributes Attributes
            {
                get
                {
                    return this._WrappedPropertyInstance.Attributes;
                }
            }
        }
    }
}
